package com.yfunc.common.persistence.defaults;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.jdbc.SQL;

import com.yfunc.common.persistence.Query;
import com.yfunc.common.persistence.ibatis.EntityInfo;
import com.yfunc.common.persistence.sql.JoinOn;
import com.yfunc.common.persistence.sql.SQLQuery;

public class DefaultQuery<T> implements Query {
	
	private SQLQuery query;
	private Builder builder;

	public DefaultQuery(Class<T> entityClass, SQLQuery query) {
		this.builder = new Builder(entityClass);
		this.query = query;
	}

	public Query first(long firstResult) {
		builder.first(firstResult);
		return this;
	}

	public Query max(long maxResult) {
		builder.max(maxResult);
		return this;
	}

	public Query and(String propertyName, Object value) {
		builder.and(propertyName, value);
		return this;
	}

	@Override
	public Query and(String propertyName, Object value, String express) {
		builder.and(propertyName, value , express);
		return this;
	}

	@Override
	public Query like(String propertyName, String value) {
		builder.like(propertyName, value);
		return this;
	}

	@Override
	public Query id(Object pk) {
		builder.id(pk);
		return this;
	}

	public Query orderAsc(String propertyName) {
		builder.order(propertyName, true);
		return this;
	}

	public Query orderDesc(String propertyName) {
		builder.order(propertyName, false);
		return this;
	}

	public Long count() {
		return new SelectQuery().count();
	}

	public List<T> list() {
		return new SelectQuery().list();
	}

	public T uniqueResult() {
		return new SelectQuery().uniqueResult();
	}

	@Override
	public T get() {
		return new SelectQuery().get();
	}

	class Value {
		public String expression;
		public Object value;

		public Value(Object value) {
			this("=", value);
		}

		public Value(String expression, Object value) {
			this.expression = expression;
			this.value = value;
		}
	}

	class Builder {

		protected Class<T> entityClass;
		protected Map<String, Value> properites;
		protected Map<String, Boolean> orders;
		protected Long firstResult;
		protected Long maxResult;
		protected Object id;

		public Builder(Class<T> entityClass) {
			this.entityClass = entityClass;
			this.properites = new LinkedHashMap<String, Value>();
			this.orders = new LinkedHashMap<String, Boolean>();
		}

		public void first(long firstResult) {
			this.firstResult = firstResult;
		}

		public void max(long maxResult) {
			this.maxResult = maxResult;
		}

		public void and(String propertyName, Object value) {
			this.properites.put(propertyName, new Value(value));
		}

		public void and(String propertyName, Object value, String express) {
			this.properites.put(propertyName, new Value(express, value));
		}

		public void like(String propertyName, String value) {
			this.properites.put(propertyName, new Value("like", value));
		}

		public void id(Object pk) {
			this.id = pk;
		}
		
		public void order(String propertyName , boolean asc){
			this.orders.put(propertyName, asc);
		}
	}

	class SelectQuery {

		public SelectQuery() {
		}

		private List<T> queryList(Long first, Long max) {
			EntityInfo entityInfo = new EntityInfo(builder.entityClass);
			SQL sql = new SQL();
			sql.SELECT(entityInfo.id());
			for (String column : entityInfo.fields()) {
				sql.SELECT(column);
			}
			sql.FROM(entityInfo.tableName());

			for (String prop : builder.properites.keySet()) {
				Value value = builder.properites.get(prop);
				sql.WHERE(prop + value.expression + " ?");
			}

			for (String prop : builder.orders.keySet()) {
				sql.ORDER_BY(builder.orders.get(prop) ? prop + " ASC " : prop
						+ " DESC ");
			}

			String sqlStr = sql.toString();
			if (first != null && max != null && max > 0) {
				sqlStr += " limit " + first + " ,  " + max;
			}

			ArrayList<Object> params = new ArrayList<Object>();
			for (Value value : builder.properites.values()) {
				params.add(value.value);
			}
			return query.queryObjects(builder.entityClass, sqlStr,
					params.toArray());
		}

		public Long count() {
			EntityInfo entityInfo = new EntityInfo(builder.entityClass);
			SQL sql = new SQL();
			sql.SELECT("COUNT(" + entityInfo.id() + ")");
			sql.FROM(entityInfo.tableName());
			for (String prop : builder.properites.keySet()) {
				Value value = builder.properites.get(prop);
				sql.WHERE(prop + value.expression + " ?");
			}

			ArrayList<Object> params = new ArrayList<Object>();
			for (Value value : builder.properites.values()) {
				params.add(value.value);
			}
			return query.queryInt(sql.toString(), params.toArray());
		}

		public List<T> list() {
			return this.queryList(builder.firstResult,
					builder.maxResult);
		}

		public T uniqueResult() {
			List<T> list = this.queryList(0L, 1L);
			return list.size() > 0 ? list.get(0) : null;
		}

		public T get() {
			EntityInfo entityInfo = new EntityInfo(builder.entityClass);
			SQL sql = new SQL();
			sql.SELECT(entityInfo.id());
			for (String column : entityInfo.fields()) {
				sql.SELECT(column);
			}
			sql.FROM(entityInfo.tableName());
			sql.WHERE(entityInfo.id() + " = ?");
			return query.queryObject(builder.entityClass, sql.toString(),
					new JoinOn(), builder.id);
		}

	}

}
